package org.monitoringtools.elements.impl;

import java.security.SecureRandom;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import junit.framework.Assert;

import org.junit.Test;

public class TimeBasedCounterTest {

    private final boolean printOutput = false;

    @Test
    public void count() {
        TimeBasedCounter counter = new TimeBasedCounter("fullIteration", TimeUnit.SECONDS, 1);
        final Random random = new Random();

        int iterations = random.nextInt(1000000);
        for (int i = 0; i < iterations; i++)
            counter.log(random.nextBoolean());

        if (printOutput)
            printValues(counter.toString(), iterations, counter.total(), counter.failure() + counter.success());

        Assert.assertEquals(iterations, counter.total());
        Assert.assertEquals(counter.total(), counter.failure() + counter.success());
    }

    @Test
    public void countResetAndCount() throws InterruptedException {
        fullIteration();
    }

    @Test
    public void countResetAndCountNTimes() throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            if (printOutput)
                System.out.println("\n\tExecution #" + (i + 1));
            fullIteration();
        }
    }

    @Test
    public void countTimeoutAndCountNTimes() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            if (printOutput)
                System.out.println("\n\tExecution #" + (i + 1));
            fullIterationByTimeout();
        }
    }

    @Test
    public void reset() throws InterruptedException {
        final String id = "fullIteration";
        final TimeBasedCounter counter = new TimeBasedCounter(id, TimeUnit.SECONDS, 1);
        final long first = counter.getLastRefreshTime();

        Assert.assertEquals(id, counter.getId());
        Assert.assertEquals(1, counter.getDelta());

        Assert.assertEquals(0, counter.failure());
        Assert.assertEquals(0, counter.success());
        Assert.assertEquals(0, counter.total());

        Assert.assertEquals(counter.getLastRefreshTime(), counter.getLastRefreshTime());

        Assert.assertEquals(0, counter.failure());
        Assert.assertEquals(0, counter.success());
        Assert.assertEquals(0, counter.total());

        Thread.sleep(1);
        counter.reset();

        Assert.assertEquals(id, counter.getId());
        Assert.assertEquals(1, counter.getDelta());

        Assert.assertEquals(0, counter.failure());
        Assert.assertEquals(0, counter.success());
        Assert.assertEquals(0, counter.total());

        Assert.assertEquals(counter.getLastRefreshTime(), counter.getLastRefreshTime());

        Assert.assertFalse(first == counter.getLastRefreshTime());
        Assert.assertTrue(first < counter.getLastRefreshTime());

        Assert.assertEquals(0, counter.failure());
        Assert.assertEquals(0, counter.success());
        Assert.assertEquals(0, counter.total());

        if (printOutput)
            System.out.println(counter.toString());
    }

    private void doStep(int id, final TimeBasedCounter counter) {
        final SecureRandom randomInt = new SecureRandom();
        final SecureRandom randomBool = new SecureRandom();

        final int iter = randomInt.nextInt(100000);
        for (int i = 0; i < iter; i++)
            counter.log(randomBool.nextBoolean());

        if (printOutput)
            printValues(new Date(), "Step #" + id, "iter: " + iter, "total: " + counter.total(), "sum: " + (counter.failure() + counter.success()),
                    counter.toString());

        Assert.assertEquals(iter, counter.total());
        Assert.assertEquals(counter.total(), counter.failure() + counter.success());
    }

    private void fullIteration() throws InterruptedException {
        final TimeBasedCounter counter = new TimeBasedCounter("fullIteration", TimeUnit.SECONDS, 1);

        doStep(1, counter);

        counter.reset();

        doStep(2, counter);
    }

    private void fullIterationByTimeout() throws InterruptedException {
        final TimeBasedCounter counter = new TimeBasedCounter("fullIterationByTimeout", TimeUnit.SECONDS, 1);

        doStep(1, counter);

        Thread.sleep(1000);

        doStep(2, counter);
    }

    private void printValues(Object... objects) {
        if (objects == null)
            return;

        final StringBuilder sb = new StringBuilder();
        for (Object object : objects)
            sb.append(object).append('\t');

        System.out.println(sb.toString());
    }
}
